#ifndef EPUBZLIB_APIS_EPUB_ZLIB_API_HPP
#define EPUBZLIB_APIS_EPUB_ZLIB_API_HPP

class EpubImplement final {
public:
  class DataSet final : private EpubContext::OnContextZipProcessListener {
  public:
    explicit DataSet(int num) : mNum(num), mContext(), mListenerParams(), mListener() {
      mContext.registerOnContextZipProcessListener(this);
    }

    ~DataSet() override {
      mContext.unregisterOnContextZipProcessListener(this);
    }

    DataSet(DataSet const &) = delete;
    DataSet &operator=(DataSet const &) = delete;

    auto num() const -> int {
      return mNum;
    }

    void set(void *params, EpubZLIB_OnZipProcessListener *listener) {
      mListenerParams = params;
      mListener       = listener;
    }

    void clean() {
      mContext.clean();
    }

    void add(std::string const &path) {
      mContext.add(path);
    }

    void compress(std::string const &path) {
      mContext.compress(path);
    }

  private:
    int mNum;
    EpubContext mContext;

    void *mListenerParams;
    EpubZLIB_OnZipProcessListener *mListener;

    void onZipProcess(const char *name, int step) override {
      if (mListener != nullptr)
        mListener(mListenerParams, name, step);
    }
  };

  static auto instance() -> EpubImplement & {
    static auto &&obj = (EpubImplement *) (nullptr);
    if (obj == nullptr)
      obj = new EpubImplement();
    return (*obj);
  }

  void create(int num) {
    auto &&mark = bool(false);

    for (auto &&item : mContexts) {
      if (item.num() != num)
        continue;
      mark = true;
      item.clean();
      break;
    }

    if (!mark)
      mContexts.emplace_back(num);
  }

  void set(int num, void *params, EpubZLIB_OnZipProcessListener *listener) {
    for (auto &&item : mContexts) {
      if (item.num() != num)
        continue;
      item.set(params, listener);
      break;
    }
  }

  void add(int num, std::string const &path) {
    for (auto &&item : mContexts) {
      if (item.num() != num)
        continue;
      item.add(path);
      break;
    }
  }

  void compress(int num, std::string const &path) {
    for (auto &&item : mContexts) {
      if (item.num() != num)
        continue;
      item.compress(path);
      break;
    }
  }

private:
  std::list<DataSet> mContexts;

  EpubImplement() : mContexts() {}
};

void EpubZLIB_Init(int num) {
  EpubImplement::instance().create(num);
}

void EpubZLIB_Set(int num, void *params, EpubZLIB_OnZipProcessListener *listener) {
  EpubImplement::instance().set(num, params, listener);
}

void EpubZLIB_Add(int num, char const *path) {
  EpubImplement::instance().add(num, path);
}

void EpubZLIB_Compress(int num, char const *path) {
  EpubImplement::instance().compress(num, path);
}

#endif//EPUBZLIB_APIS_EPUB_ZLIB_API_HPP
